using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Threading;
using System.Data;

namespace SSLScavenger
{
    public class Ware
    {
        public string ServerName;
        public Ware(string _strServerName)
        {
            ServerName = _strServerName;
        }
    }

    class Wrapper
    {
        public int QueueLength;
        public DataBase DB;
        public Wrapper()
        {
            QueueLength = 0;
            DataBase _DB = new DataBase();
            DB = _DB;
        }
        public void Produce(Ware ware)
        {
            //ThreadPool.SetMaxThreads(10,10);
            ThreadPool.QueueUserWorkItem(new WaitCallback(Consume), ware);
            QueueLength++;
        }
        //public DataSet dataset;
        public void Consume(Object obj)
        {
            Ware ware = (Ware)obj;
            try
            {
                CertificateValidator CV = new CertificateValidator();
                CV.Validate(ware.ServerName,DB);
                //dataset = ((DataBase)DB).dstResults;
            }
            catch (Exception e)
            {
                QueueLength--;
                return;
            }
            //Console.WriteLine("aa");
            QueueLength--;

        }
    }
    public class DataBase
    {
        public DataSet dstResults;
        private DataTable tblResults;
        public DataBase()
        {        
        DataSet _dstResults = new DataSet("dstResults");
        DataTable _tblResults = new DataTable("Results");
        _dstResults.Tables.Add(_tblResults);
        _tblResults.Columns.Add("ServerName"); _tblResults.Columns.Add("IssuerName"); _tblResults.Columns.Add("Subject");
        _tblResults.Columns.Add("Expiration"); _tblResults.Columns.Add("End Point Name"); _tblResults.Columns.Add("Days to Expire");
        dstResults = _dstResults;
        tblResults = _tblResults;

        }
        public void AddRow(X509ChainElement _CE, string _ServerName, string _OriginalName)
        {
            if (((TimeSpan)(_CE.Certificate.NotAfter - DateTime.Now)).Days < 30)
            {
                DataRow dr = tblResults.NewRow();
                dr["Days to Expire"] = ((TimeSpan)(_CE.Certificate.NotAfter - DateTime.Now)).Days;
                dr["ServerName"] = _ServerName;
                dr["IssuerName"] = _CE.Certificate.Issuer;
                dr["Subject"] = _CE.Certificate.Subject;
                dr["Expiration"] = _CE.Certificate.NotAfter;
                dr["End Point Name"] = _OriginalName;
                //TimeSpan diff = _CE.Certificate.NotAfter - DateTime.Now;
                //dr["Days to Expire"] = diff.Days;
                tblResults.Rows.Add(dr);
            }
        }
    }

    class CertificateValidator
    {
        public X509ChainElement Element;
        string _OriginalName;
        string _serverName;
        
        public bool Validate(string serverName, DataBase _DB)
        {
            TcpClient client = new TcpClient();
            _serverName = serverName;
            Console.WriteLine("Trying to connect to " + serverName);
            client.Connect(serverName, 443);
            Console.WriteLine("Client connected to " + serverName);
            SslStream sslStream = new SslStream(client.GetStream(),false,new RemoteCertificateValidationCallback(ValidateServerCertificate),null);
            Console.WriteLine("Trying to retreive SSL certificate from " + serverName);
            try
            {
                sslStream.AuthenticateAsClient(serverName);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message + ":Server " +serverName.ToString()  );
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message + ":Server " + serverName.ToString());
                }
                Console.WriteLine("Authentication failed - closing the connection." + ":Server " + serverName.ToString());
                client.Close();
                return false;
            }
            _DB.AddRow(Element, serverName, _OriginalName);
            return true;

        }


        // The following method is invoked by the RemoteCertificateValidationDelegate.
        public bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
            if (! sslPolicyErrors.ToString().Contains("RemoteCertificateChainErrors")){
                Console.WriteLine("SSL certificate do not have Chain Errors " + _serverName);
                return false;
            }
            try
            {
                object  MyCert = new object();
                string strOriginalName ="";
                Console.WriteLine("SSL certificate chain is retrived from " + _serverName);
                foreach (X509ChainElement cert in chain.ChainElements)
                {
                    if ( !(MyCert is X509ChainElement ) )
                    {
                        MyCert = (X509ChainElement) cert;
                        strOriginalName = ((X509ChainElement)MyCert).Certificate.Subject;
                        //firsttime = false;
                        Console.WriteLine("Original Cert is " + strOriginalName);
                        continue;

                    }
                    else if (cert.Certificate.NotAfter < ((X509ChainElement)MyCert).Certificate.NotAfter)
                    {
                        MyCert = (X509ChainElement) cert;
                    }
                }
                Element = ((X509ChainElement)MyCert);
                _OriginalName = strOriginalName;
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
        }

    }
    
}
